﻿using ProtoBuf;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace RedisDemo
{
    [ProtoContract]
    public class POCOEntity
    {
        [ProtoMember(1)]
        public int Id { get; set; }
        [ProtoMember(2)]
        public String Name { get; set; }
        [ProtoMember(3)]
        public String Name1 { get; set; }
        [ProtoMember(4)]
        public String Name2 { get; set; }
        [ProtoMember(5)]
        public String Name3 { get; set; }
        [ProtoMember(6)]
        public String Name4 { get; set; }
        [ProtoMember(7)]
        public String Name5 { get; set; }
        [ProtoMember(8)]
        public String Name6 { get; set; }
        [ProtoMember(9)]
        public String Name7 { get; set; }
        [ProtoMember(10)]
        public String Name8 { get; set; }
        [ProtoMember(11)]
        public String Name9 { get; set; }
        [ProtoMember(12)]
        public String Name10 { get; set; }
        //public String Name11 { get; set; }
        //public String Name12 { get; set; }
        //public String Name13 { get; set; }
        //public String Name14 { get; set; }
        //public String Name15 { get; set; }
        //public String Name16 { get; set; }
        //public String Name17 { get; set; }
        //public String Name18 { get; set; }
        //public String Name19 { get; set; }
        //public String Name20 { get; set; }
        //public String Name21 { get; set; }
        //public String Name22 { get; set; }
        //public String Name23 { get; set; }
        //public String Name24 { get; set; }
        //public String Name25 { get; set; }
        //public String Name26 { get; set; }
        //public String Name27 { get; set; }
        //public String Name28 { get; set; }
        //public String Name29 { get; set; }
        //public String Name30 { get; set; }
        //public String Name31 { get; set; }
        //public String Name32 { get; set; }
        //public String Name33 { get; set; }
        //public String Name34 { get; set; }
        //public String Name35 { get; set; }
        //public String Name36 { get; set; }
        //public String Name37 { get; set; }
        //public String Name38 { get; set; }
        //public String Name39 { get; set; }
        //public String Name40 { get; set; }
        //public String Name41 { get; set; }
        //public String Name42 { get; set; }
        //public String Name43 { get; set; }
        //public String Name44 { get; set; }
        //public String Name45 { get; set; }
        //public String Name46 { get; set; }
        //public String Name47 { get; set; }
        //public String Name48 { get; set; }
        //public String Name49 { get; set; }
        //public String Name50 { get; set; }
        //public String Name51 { get; set; }
        //public String Name52 { get; set; }
        //public String Name53 { get; set; }
        //public String Name54 { get; set; }
        //public String Name55 { get; set; }
        //public String Name56 { get; set; }
        //public String Name57 { get; set; }
        //public String Name58 { get; set; }
        //public String Name59 { get; set; }
        //public String Name60 { get; set; }
        //public String Name61 { get; set; }
        //public String Name62 { get; set; }
        //public String Name63 { get; set; }
        //public String Name64 { get; set; }
        //public String Name65 { get; set; }
        //public String Name66 { get; set; }
        //public String Name67 { get; set; }
        //public String Name68 { get; set; }
        //public String Name69 { get; set; }
        //public String Name70 { get; set; }
        //public String Name71 { get; set; }
        //public String Name72 { get; set; }
        //public String Name73 { get; set; }
        //public String Name74 { get; set; }
        //public String Name75 { get; set; }
        //public String Name76 { get; set; }
        //public String Name77 { get; set; }
        //public String Name78 { get; set; }
        //public String Name79 { get; set; }
        //public String Name80 { get; set; }
        //public String Name81 { get; set; }
        //public String Name82 { get; set; }
        //public String Name83 { get; set; }
        //public String Name84 { get; set; }
        //public String Name85 { get; set; }
        //public String Name86 { get; set; }
        //public String Name87 { get; set; }
        //public String Name88 { get; set; }
        //public String Name89 { get; set; }
        //public String Name90 { get; set; }
        //public String Name91 { get; set; }
        //public String Name92 { get; set; }
        //public String Name93 { get; set; }
        //public String Name94 { get; set; }
        //public String Name95 { get; set; }
        //public String Name96 { get; set; }
        //public String Name97 { get; set; }
        //public String Name98 { get; set; }
        //public String Name99 { get; set; }
        //public String Name100 { get; set; }

    }


    //public class EntityDictMapper
    //{
    //    private delegate T mapEntity<T>(Dictionary<string, string> dic);
    //    private static ConcurrentDictionary<Type, Delegate> cachedMappers = new ConcurrentDictionary<Type, Delegate>();

    //    private static ConcurrentDictionary<Type, Delegate> cachedMappers_Dict = new ConcurrentDictionary<Type, Delegate>();
    //    private delegate Dictionary<string, string> mapDictionary<T>(T entity);

    //    public static T MapToEntities<T>(Dictionary<string, string> dic)
    //    {
    //        Delegate mapDelegate; 
    //        // If a mapping function from dr -> T does not exist, create and cache one
    //        if (cachedMappers.TryGetValue(typeof(T),out mapDelegate))//(!cachedMappers.ContainsKey(typeof(T)))
    //        {
    //            // Our method will take in a single parameter, a DbDataReader
    //            Type[] methodArgs = { typeof(Dictionary<string, string>) };

    //            // The MapDR method will map a DbDataReader row to an instance of type T
    //            DynamicMethod dm = new DynamicMethod("MapDic", typeof(T), methodArgs, Assembly.GetExecutingAssembly().GetType().Module);
    //            ILGenerator il = dm.GetILGenerator();

    //            // We'll have a single local variable, the instance of T we're mapping
    //            il.DeclareLocal(typeof(T));

    //            // Create a new instance of T and save it as variable 0
    //            il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
    //            il.Emit(OpCodes.Stloc_0);

    //            foreach (PropertyInfo pi in typeof(T).GetProperties())
    //            {
    //                // Load the T instance, SqlDataReader parameter and the field name onto the stack
    //                il.Emit(OpCodes.Ldloc_0);
    //                il.Emit(OpCodes.Ldarg_0);
    //                il.Emit(OpCodes.Ldstr, pi.Name);

    //                // Push the column value onto the stack
    //                il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, string>).GetMethod("get_Item", new Type[] { typeof(string) }));

    //                // Depending on the type of the property, convert the datareader column value to the type
    //                switch (pi.PropertyType.Name)
    //                {
    //                    case "Int16":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt16", new Type[] { typeof(object) }));
    //                        break;
    //                    case "Int32":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt32", new Type[] { typeof(object) }));
    //                        break;
    //                    case "Int64":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt64", new Type[] { typeof(object) }));
    //                        break;
    //                    case "Boolean":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToBoolean", new Type[] { typeof(object) }));
    //                        break;
    //                    case "String":
    //                        il.Emit(OpCodes.Callvirt, typeof(string).GetMethod("ToString", new Type[] { }));
    //                        break;
    //                    case "DateTime":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToDateTime", new Type[] { typeof(object) }));
    //                        break;
    //                    case "Decimal":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToDecimal", new Type[] { typeof(object) }));
    //                        break;
    //                    default:
    //                        // Don't set the field value as it's an unsupported type
    //                        continue;
    //                }

    //                // Set the T instances property value
    //                il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("set_" + pi.Name, new Type[] { pi.PropertyType }));
    //            }

    //            // Load the T instance onto the stack
    //            il.Emit(OpCodes.Ldloc_0);

    //            // Return
    //            il.Emit(OpCodes.Ret);
    //            mapDelegate = dm.CreateDelegate(typeof(mapEntity<T>));
    //            // Cache the method so we won't have to create it again for the type T
    //            cachedMappers.TryAdd(typeof(T), mapDelegate);
    //        }

    //        // Get a delegate reference to the dynamic method
    //        mapEntity<T> invokeMapEntity = (mapEntity<T>)mapDelegate;//cachedMappers[typeof(T)];
    //        return invokeMapEntity(dic);
    //    }

    //    public static Dictionary<string, string> MapToDictionary<T>(T entity, params string[] properties)
    //    {

    //        Delegate mapDelegate; 
    //        // If a mapping function from T -> Dictionary<string,string> does not exist, create and cache one
    //        if (!cachedMappers_Dict.TryGetValue(typeof(T),out mapDelegate))
    //        {
    //            // Our method will take in a single parameter, a POCO entity of T
    //            Type[] methodArgs = { typeof(T),typeof(string) };

    //            // The MapDR method will map a DbDataReader row to an instance of type T
    //            DynamicMethod dm = new DynamicMethod("MapPOCO", typeof(Dictionary<string, string>), methodArgs, Assembly.GetExecutingAssembly().GetType().Module);
    //            ILGenerator il = dm.GetILGenerator();

    //            // We'll have a single local variable, the instance of dictionary  we're mapping
    //            il.DeclareLocal(typeof(Dictionary<string, string>));

    //            // Create a new instance of T and save it as variable 0
    //            il.Emit(OpCodes.Newobj, typeof(Dictionary<string, string>).GetConstructor(Type.EmptyTypes));
    //            il.Emit(OpCodes.Stloc_0);

    //            foreach (string pname in properties)
    //            {
    //                PropertyInfo pi = typeof(T).GetProperty(pname);
    //                // Load the dictionary instance, T parameter and the field name onto the stack
    //                il.Emit(OpCodes.Ldloc_0);
    //                il.Emit(OpCodes.Ldstr, pi.Name);
    //                il.Emit(OpCodes.Ldarg_0);


    //                //Push the property value onto the stack 
    //                il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("get_" + pi.Name, new Type[] { }));

    //                switch (pi.PropertyType.Name)
    //                {
    //                    case "Int16":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToString", new Type[] { typeof(Int16) }));
    //                        break;
    //                    case "Int32":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToString", new Type[] { typeof(Int32) }));
    //                        break;
    //                    case "Int64":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToString", new Type[] { typeof(Int64) }));
    //                        break;
    //                    case "Boolean":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToString", new Type[] { typeof(Boolean) }));
    //                        break;
    //                    case "String":
    //                        il.Emit(OpCodes.Callvirt, typeof(string).GetMethod("ToString", new Type[] { }));
    //                        break;
    //                    case "DateTime":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToString", new Type[] { typeof(DateTime) }));
    //                        break;
    //                    case "Decimal":
    //                        il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToString", new Type[] { typeof(Decimal) }));
    //                        break;
    //                    default:
    //                        // Don't set the field value as it's an unsupported type
    //                        continue;
    //                }

    //                //add T instance field with value into dictionary
    //                il.Emit(OpCodes.Callvirt, typeof(Dictionary<string, string>).GetMethod("Add", new Type[] { typeof(string), typeof(string) }));

    //            }

    //            // Load the dictionary instance onto the stack
    //            il.Emit(OpCodes.Ldloc_0);

    //            // Return
    //            il.Emit(OpCodes.Ret);
    //            mapDelegate =  dm.CreateDelegate(typeof(mapDictionary<T>));
    //            // Cache the method so we won't have to create it again for the type T
    //            cachedMappers_Dict.TryAdd(typeof(T),mapDelegate);
    //        }

    //        // Get a delegate reference to the dynamic method
    //        mapDictionary<T> invokeMapDictionary = (mapDictionary<T>)mapDelegate;// cachedMappers_Dict[typeof(T)];
    //        return invokeMapDictionary(entity);
    //    }
    //    public static void ClearCachedMapperMethods()
    //    {
    //        cachedMappers.Clear();
    //    }
    //}

}
